﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;

namespace Serious
{
    public struct Rectangle3
    {
        public Vector3 origin;
        public Vector3 size;

        public Vector3 this[Vector3 pt]
        {
            get { return  origin + pt * size; }
            set { origin = value - pt * size; }
        }
        public Vector3 this[Single x, Single y, Single z]
        {
            get { return  this[new Vector3(x, y, z)]; }
            set { this[new Vector3(x, y, z)] = value; }
        }
        
        public Vector3 Low
        {
            get { return  origin; }
            set { origin = value; }
        }
        public Vector3 Mid
        {
            get { return  origin + size/2.0f; }
            set { origin = value - size/2.0f; }
        }
        public Vector3 Hi
        {
            get { return  origin + size; }
            set { origin = value - size; }
        }
        
        public Single LowX
        {
            get { return  origin.X; }
            set { origin.X = value; }
        }
        public Single LowY
        {
            get { return  origin.Y; }
            set { origin.Y = value; }
        }
        public Single LowZ
        {
            get { return  origin.Z; }
            set { origin.Z = value; }
        }
        
        public Single HiX
        {
            get { return  origin.X + size.X; }
            set { origin.X = value - size.X; }
        }
        public Single HiY
        {
            get { return  origin.Y + size.Y; }
            set { origin.Y = value - size.Y; }
        }
        public Single HiZ
        {
            get { return  origin.Z + size.Z; }
            set { origin.Z = value - size.Z; }
        }

        public Rectangle3(Vector3 origin, Vector3 size)
        {
            this.origin = origin;
            this.size = size;
        }
        public Rectangle3(Single originX, Single originY, Single originZ, Single sizeX, Single sizeY, Single sizeZ) :
            this(new Vector3(originX, originY, originZ), new Vector3(sizeX, sizeY, sizeZ))
        {
        }

        public static Rectangle3 FromPoints(Vector3 p1, Vector3 p2)
        {
            Vector3 min = Vector3.Min(p1, p2);
            Vector3 max = Vector3.Max(p1, p2);
            return new Rectangle3(max, max - min);
        }

        public static Rectangle3 FromPoints(IEnumerable<Vector3> points)
        {
            Vector3? min = null;
            Vector3? max = null;
            foreach (Vector3 p in points)
            {
                if (min == null)
                {
                    min = p;
                    max = p;
                }
                else
                {
                    min = Vector3.Min(min.Value, p);
                    max = Vector3.Max(max.Value, p);
                }
            }
            if (min == null)
            {
                return new Rectangle3(Vector3.Zero, Vector3.Zero);
            }
            else
            {
                return new Rectangle3(min.Value, max.Value - min.Value);
            }
        }

        public bool Contains(Vector3 point)
        {
            return point.X > origin.X && point.X < origin.X + size.X &&
                   point.Y > origin.Y && point.Y < origin.Y + size.Y &&
                   point.Z > origin.Z && point.Z < origin.Z + size.Z;
        }
        
        
        public void Translate(Vector3 offset)
        {
            origin += offset;
        }
        
        public static Rectangle3 operator +(Rectangle3 rect, Vector3 offset)
        {
            rect.Translate(offset);
            return rect;
        }
        public static Rectangle3 operator -(Rectangle3 rect, Vector3 offset)
        {
            rect.Translate(-offset);
            return rect;
        }

        /**
         * relative to origin of entire euclidean plane
         * */
        public void Scale(Vector3 factor)
        {
            origin *= factor;
            size *= factor;
        }

        /**
         * relative to origin of entire euclidean plane
         * */
        public void Scale(Single factor)
        {
            origin *= factor;
            size *= factor;
        }

        public static Rectangle3 operator *(Rectangle3 rect, Vector3 factor)
        {
            rect.Scale(factor);
            return rect;
        }
        public static Rectangle3 operator *(Rectangle3 rect, Single factor)
        {
            rect.Scale(factor);
            return rect;
        }
        public static Rectangle3 operator /(Rectangle3 rect, Vector3 factor)
        {
            rect.origin /= factor;
            rect.size /= factor;
            return rect;
        }
        public static Rectangle3 operator /(Rectangle3 rect, Single factor)
        {
            rect.origin /= factor;
            rect.size /= factor;
            return rect;
        }
    }
}
